﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DigitsExtractor;
using DigitsRecognizer.BussinesLogic;
using NeuralNetwork;

namespace DigitsRecognizer.UI
{
    public class TestForm : Form
    {
        private string Login = "piotrek";
        private Label DescriptionLabel, DefectEdgeLabel;
        private TextBox TextBox;
        private Button TestDrawnButton, CancelButton, TestLoadedButton, ClearButton;
        private FileDialog FileDialog;

        private float[] PictureSize;
        private float[] DrawAreaCoord;
        private DrawField[,] DrawArea;
        private bool ReadyToDraw;
        private bool IsKeyPressed;
        private DigitExtractor DigitExtractor;

        /* rysowanie */
        private Graphics Surface;
        private Pen BlackPen;
        /* pędzel do wypełnienia figury wewnątrz obiektu */
        private SolidBrush WhiteBrush, BlackBrush;
        private Bitmap PixelBitmap;

        private NeuralNetwork<double> NeuralNetwork;

        public TestForm() 
        {
            FillConstans(); 
            this.Height = 300;
            this.Width = 300;
            this.CenterToScreen();
            this.MinimizeBox = false;
            this.MaximizeBox = false;
            this.Text = "Testowanie";

            DescriptionLabel = new Label();
            DescriptionLabel.AutoSize = true;
            DescriptionLabel.Text = "Testowanie";
            DescriptionLabel.Location = new Point(100, 7);
            DescriptionLabel.Font = new Font(new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif), 10, FontStyle.Italic);

            DefectEdgeLabel = new Label();
            DefectEdgeLabel.AutoSize = true;
            DefectEdgeLabel.Text = "Próg błędu: ";
            DefectEdgeLabel.Location = new Point(18, 195);

            TextBox = new TextBox();
            TextBox.AutoSize = true;
            TextBox.Text = "0,001";
            TextBox.TextAlign = HorizontalAlignment.Center;
            TextBox.Location = new Point(105, 190);
            TextBox.Width = 80;

            ClearButton = new Button();
            ClearButton.Text = "Wyczyść";
            ClearButton.Location = new Point(202, 189);

            TestDrawnButton = new Button();
            TestDrawnButton.Text = "Testuj wpisaną";
            TestDrawnButton.Location = new Point(5, 230);
            TestDrawnButton.Width = 90;

            TestLoadedButton = new Button();
            TestLoadedButton.Text = "Testuj wczytaną";
            TestLoadedButton.Location = new Point(102, 230);
            TestLoadedButton.Width = 94;

            CancelButton = new Button();
            CancelButton.Text = "Powrót";
            CancelButton.Location = new Point(202, 230);

            FileDialog = new OpenFileDialog();
            FileDialog.Title = "Wczytaj plik do testowania";
            FileDialog.InitialDirectory = Application.StartupPath;
            FileDialog.Filter = "Image File For Testing(*.BMP;)|*.BMP;|All files (*.*)|*.*";

            this.Controls.Add(DescriptionLabel);
            this.Controls.Add(DefectEdgeLabel);
            this.Controls.Add(TextBox);
            this.Controls.Add(TestDrawnButton);
            this.Controls.Add(ClearButton);
            this.Controls.Add(TestLoadedButton);
            this.Controls.Add(CancelButton);

            // Rysowanie miejsca do wpisywania cyfry
            Surface = this.CreateGraphics();
            BlackPen = new Pen(Color.Black, 1.0f);
            WhiteBrush = new SolidBrush(Color.White);
            BlackBrush = new SolidBrush(Color.Black);  

            /* obsługa wciśnięcia przycisku Cancel */
            EventHandler cancelButtonEH = new EventHandler(OnClickCancel);
            CancelButton.Click += cancelButtonEH;

            /* obsługa wciśnięcia przycisku Load */
            EventHandler testLoadedButtonEH = new EventHandler(OnClickTestLoaded);
            TestLoadedButton.Click += testLoadedButtonEH;

            /* obsługa wciśnięcia przycisku Test */
            EventHandler testDrawnButtonEH = new EventHandler(OnClickTestDrawn);
            TestDrawnButton.Click += testDrawnButtonEH;

            /* obsługa wciśnięcia przycisku Clear */
            EventHandler clearButtonEH = new EventHandler(OnClickClear);
            ClearButton.Click += clearButtonEH;

            this.KeyPreview = true;
            this.KeyDown += new KeyEventHandler(OnKeyDown);
            this.KeyUp += new KeyEventHandler(OnKeyUp);
            this.Paint += new PaintEventHandler(OnPaint);
            this.MouseDown += new MouseEventHandler(OnMouseDown);
            this.MouseUp += new MouseEventHandler(OnMouseUp);
        }

        public TestForm(string login)
            : this()
        {
            Login = login;
        }

        private void OnClickCancel(object sender, EventArgs args)
        {
            this.Close();
        }

        private void OnClickTestLoaded(object sender, EventArgs args)
        {
            // test na podstawie wczytanego pliku
            FileDialog.ShowDialog();
            double range = Convert.ToDouble(TextBox.Text);
            double result = NeuralNetwork.EstimateOutput(DigitExtractor.LoadDigit(FileDialog.FileName), Login, range);
            MessageBox.Show(string.Format("Wartość wczytanej cyfry wynosi {0}", result), "Estymacja wartości wczytanej cyfry", MessageBoxButtons.OK, MessageBoxIcon.Information);
            
            FileDialog.FileName = ""; 
        }

        private void OnClickTestDrawn(object sender, EventArgs args)
        {
            // Test na podstawie wpisanej cyfry
            double range = Convert.ToDouble(TextBox.Text);
            double result = NeuralNetwork.EstimateOutput(DigitExtractor.ConvertToByte(DrawArea, (int)PictureSize[0], (int)PictureSize[1]), Login, range);
            if (result == -1)
            {
                MessageBox.Show(string.Format("Wśród stworzonych wzorców nie rozpoznano wpisanej cyfry", result), "Estymacja wartości wpisanej cyfry", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                MessageBox.Show(string.Format("Wartość wpisanej cyfry wynosi {0}", result), "Estymacja wartości wpisanej cyfry", MessageBoxButtons.OK, MessageBoxIcon.Information);
            } 
            OnClickClear(this, new EventArgs());
        }

        private void OnClickClear(object sender, EventArgs args)
        {
            Clear();
            OnPaint(this, new PaintEventArgs(Surface, this.ClientRectangle));
        }

        private void OnPaint(object sender, PaintEventArgs e)
        {
            DrawBackground();
            DrawDigit();
        }

        private void OnMouseDown(object sender, MouseEventArgs e)
        {
            if (e.X >= DrawArea[0, 0].Coordinates.X && e.X <= DrawArea[0, (int)PictureSize[0] - 1].Coordinates.X &&
                e.Y >= DrawArea[0, 0].Coordinates.Y && e.Y <= DrawArea[(int)PictureSize[1] - 1, 0].Coordinates.Y)
            {
                ReadyToDraw = true;
                SetPixel(new Point(e.X, e.Y));
                this.Paint -= new PaintEventHandler(OnPaint);
                this.MouseMove += new MouseEventHandler(OnMouseMove);
            }
        }

        private void OnMouseUp(object sender, MouseEventArgs e)
        {
            this.MouseMove -= new MouseEventHandler(OnMouseMove);
            OnPaint(this, new PaintEventArgs(Surface, this.ClientRectangle));
            ReadyToDraw = false;
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.X >= DrawArea[0, 0].Coordinates.X && e.X <= DrawArea[0, (int)PictureSize[0] - 1].Coordinates.X &&
                e.Y >= DrawArea[0, 0].Coordinates.Y && e.Y <= DrawArea[(int)PictureSize[1] - 1, 0].Coordinates.Y && ReadyToDraw)
            {
                ReadyToDraw = true;
                SetPixel(new Point(e.X, e.Y));
            }
            else 
            {
                ReadyToDraw = false;
            }
        }

        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ControlKey)
            {
                IsKeyPressed = true;
            }
        }

        private void OnKeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ControlKey)
            {
                IsKeyPressed = false;
            }
        }

        private void DrawBackground()
        {
            Surface.FillRectangle(BlackBrush, DrawArea[0, 0].Coordinates.X - 1, DrawArea[0, 0].Coordinates.Y - 1, PictureSize[0] + 2, PictureSize[1] + 2);
            Surface.FillRectangle(WhiteBrush, DrawArea[0, 0].Coordinates.X, DrawArea[0, 0].Coordinates.Y, PictureSize[0], PictureSize[1]); 
        }

        private void FillConstans() 
        {
            NeuralNetwork = new NeuralNetwork<double>();
            DigitExtractor = new DigitExtractor();
            PixelBitmap = new Bitmap(1, 1);
            PixelBitmap.SetPixel(0, 0, Color.Black);
            PictureSize = new float[] { 66.0f, 100.0f };
            DrawAreaCoord = new float[] { 110.0f, 55.0f, 110.0f + PictureSize[0], 55.0f + PictureSize[1] };
            DrawArea = new DrawField[(int)PictureSize[1], (int)PictureSize[0]];
            for (int i = 0; i < (int)PictureSize[1]; i++) 
            {
                for (int j = 0; j < (int)PictureSize[0]; j++)
                {
                    DrawArea[i, j] = new DrawField(new Point((int)DrawAreaCoord[0] + j, (int)DrawAreaCoord[1] + i));
                }
            }
        }

        private void SetPixel(Point point) 
        {
            for (int i = 0; i < (int)PictureSize[1]; i++)
            {
                for (int j = 0; j < (int)PictureSize[0]; j++)
                {
                    if (DrawArea[i, j] == point)
                    {
                        DrawArea[i, j].IsDrawn = IsKeyPressed ? false : true;

                        if (i - 1 > 0)
                        {
                            DrawArea[i - 1, j].IsDrawn = IsKeyPressed ? false : true;
                        }
                        if (i + 1 < (int)PictureSize[1])
                        {
                            DrawArea[i + 1, j].IsDrawn = IsKeyPressed ? false : true;
                        }
                        if (j - 1 > 0)
                        {
                            DrawArea[i, j - 1].IsDrawn = IsKeyPressed ? false : true;
                        }
                        if (j + 1 < (int)PictureSize[0])
                        {
                            DrawArea[i, j + 1].IsDrawn = IsKeyPressed ? false : true;
                        }
                        break;
                    }
                }
            }
        }

        private void DrawDigit() 
        {
            for (int i = 0; i < (int)PictureSize[1]; i++)
            {
                for (int j = 0; j < (int)PictureSize[0]; j++)
                {
                    if(DrawArea[i, j].IsDrawn)
                    {
                        Surface.DrawImageUnscaled(PixelBitmap,DrawArea[i, j].Coordinates);
                    }
                }
            }
        }

        private void Clear() 
        {
            for (int i = 0; i < (int)PictureSize[1]; i++)
            {
                for (int j = 0; j < (int)PictureSize[0]; j++)
                {
                    if (DrawArea[i, j].IsDrawn)
                    {
                        DrawArea[i, j].IsDrawn = false;
                    }
                }
            }
        }

        private byte[] ConvertToByte() 
        { 
            byte[] tab = new byte[(int)PictureSize[1]*(int)PictureSize[0]];
            int index = 0;
            for (int i = 0; i < (int)PictureSize[1]; i++) 
            {
                for (int j = 0; j < (int)PictureSize[0]; j++, index++)
                {
                    tab[index] = DrawArea[i, j].IsDrawn ? Convert.ToByte(1) : Convert.ToByte(0);
                }
            }
            return tab;
        }
    }
}
